/*
 * SQLDA 支持例程
 *
 * 由 sqlda 指针指向的分配内存区域
 * 包含元数据和数据，因此释放
 * 的方式是简单的 free(sqlda)，正如 ESQL/C 示例所期望的那样。
 */

#define POSTGRES_ECPG_INTERNAL
#include "postgres_fe.h"

#include "catalog/pg_type_d.h"
#include "decimal.h"
#include "ecpg-pthread-win32.h"
#include "ecpgerrno.h"
#include "ecpglib.h"
#include "ecpglib_extern.h"
#include "ecpgtype.h"
#include "sqlca.h"
#include "sqlda-compat.h"
#include "sqlda-native.h"

/*
 * 通过当前变量的大小和对齐计算下一个变量的偏移。
 *
 *
 * 返回：
 * - 当前变量的偏移在 *current
 * - 下一个变量的偏移在 *next
 */
static void fc_ecpg_sqlda_align_add_size(long fc_offset, int fc_alignment, int fc_size, long *fc_current, long *fc_next)
{
	if (fc_offset % fc_alignment)
		fc_offset += fc_alignment - (fc_offset % fc_alignment);
	if (fc_current)
		*fc_current = fc_offset;
	fc_offset += fc_size;
	if (fc_next)
		*fc_next = fc_offset;
}

static long fc_sqlda_compat_empty_size(const PGresult *fc_res)
{
	long		fc_offset;
	int			fc_i;
	int			fc_sqld = PQnfields(fc_res);

	/* 存储主结构和字段结构的初始大小 */
	fc_offset = sizeof(struct sqlda_compat) + fc_sqld * sizeof(struct sqlvar_compat);

	/* 为字段名称添加空间 */
	for (fc_i = 0; fc_i < fc_sqld; fc_i++)
		fc_offset += strlen(PQfname(fc_res, fc_i)) + 1;

	/* 为第一个字段值添加填充 */
	fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), 0, &fc_offset, NULL);

	return fc_offset;
}

static long fc_sqlda_common_total_size(const PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat, long fc_offset)
{
	int			fc_sqld = PQnfields(fc_res);
	int			fc_i;
	long		fc_next_offset;

	/* 为字段值添加空间 */
	for (fc_i = 0; fc_i < fc_sqld; fc_i++)
	{
		enum ECPGttype fc_type = sqlda_dynamic_type(PQftype(fc_res, fc_i), fc_compat);

		switch (fc_type)
		{
			case ECPGt_short:
			case ECPGt_unsigned_short:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(short), sizeof(short), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_int:
			case ECPGt_unsigned_int:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), sizeof(int), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_long:
			case ECPGt_unsigned_long:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(long), sizeof(long), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_long_long:
			case ECPGt_unsigned_long_long:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(long long), sizeof(long long), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_bool:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(bool), sizeof(bool), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_float:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(float), sizeof(float), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_double:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(double), sizeof(double), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_decimal:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), sizeof(decimal), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_numeric:

				/*
				 * 我们将数字结构对齐以允许其存储指针，
				 * 同时数字数组对齐到 int（这似乎有些过度，
				 * 但我们还是保持兼容性）。
				 *
				 * 不幸的是，我们需要两次拆解值以
				 * 找到数字数组的大小，然后再填充它。
				 */
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(NumericDigit *), sizeof(numeric), &fc_offset, &fc_next_offset);
				if (!PQgetisnull(fc_res, fc_row, fc_i))
				{
					char	   *fc_val = PQgetvalue(fc_res, fc_row, fc_i);
					numeric    *fc_num;

					fc_num = PGTYPESnumeric_from_asc(fc_val, NULL);
					if (!fc_num)
						break;
					if (fc_num->buf)
						fc_ecpg_sqlda_align_add_size(fc_next_offset, sizeof(int), fc_num->digits - fc_num->buf + fc_num->ndigits, &fc_offset, &fc_next_offset);
					PGTYPESnumeric_free(fc_num);
				}
				break;
			case ECPGt_date:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(date), sizeof(date), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_timestamp:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int64), sizeof(timestamp), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_interval:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int64), sizeof(interval), &fc_offset, &fc_next_offset);
				break;
			case ECPGt_char:
			case ECPGt_unsigned_char:
			case ECPGt_string:
			default:
				{
					long		fc_datalen = strlen(PQgetvalue(fc_res, fc_row, fc_i)) + 1;

					fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), fc_datalen, &fc_offset, &fc_next_offset);
					break;
				}
		}
		fc_offset = fc_next_offset;
	}
	return fc_offset;
}


static long fc_sqlda_compat_total_size(const PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat)
{
	long		fc_offset;

	fc_offset = fc_sqlda_compat_empty_size(fc_res);

	if (fc_row < 0)
		return fc_offset;

	fc_offset = fc_sqlda_common_total_size(fc_res, fc_row, fc_compat, fc_offset);
	return fc_offset;
}

static long fc_sqlda_native_empty_size(const PGresult *fc_res)
{
	long		fc_offset;
	int			fc_sqld = PQnfields(fc_res);

	/* 存储主结构和字段结构的初始大小 */
	fc_offset = sizeof(struct sqlda_struct) + (fc_sqld - 1) * sizeof(struct sqlvar_struct);

	/* 为第一个字段值添加填充 */
	fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), 0, &fc_offset, NULL);

	return fc_offset;
}

static long fc_sqlda_native_total_size(const PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat)
{
	long		fc_offset;

	fc_offset = fc_sqlda_native_empty_size(fc_res);

	if (fc_row < 0)
		return fc_offset;

	fc_offset = fc_sqlda_common_total_size(fc_res, fc_row, fc_compat, fc_offset);
	return fc_offset;
}

/*
 * 从 PGresult 构建 "struct sqlda_compat"（仅元数据）
 * 在给定行号中留出足够的字段值空间
 */
struct sqlda_compat *
ecpg_build_compat_sqlda(int fc_line, PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat)
{
	struct sqlda_compat *fc_sqlda;
	struct sqlvar_compat *fc_sqlvar;
	char	   *fc_fname;
	long		fc_size;
	int			fc_sqld;
	int			fc_i;

	fc_size = fc_sqlda_compat_total_size(fc_res, fc_row, fc_compat);
	fc_sqlda = (struct sqlda_compat *) ecpg_alloc(fc_size, fc_line);
	if (!fc_sqlda)
		return NULL;

	memset(fc_sqlda, 0, fc_size);
	fc_sqlvar = (struct sqlvar_compat *) (fc_sqlda + 1);
	fc_sqld = PQnfields(fc_res);
	fc_fname = (char *) (fc_sqlvar + fc_sqld);

	fc_sqlda->sqld = fc_sqld;
	ecpg_log("ecpg_build_compat_sqlda on line %d sqld = %d\n", fc_line, fc_sqld);
	fc_sqlda->desc_occ = fc_size;		/* 在这里作弊，保留完整的分配大小 */
	fc_sqlda->sqlvar = fc_sqlvar;

	for (fc_i = 0; fc_i < fc_sqlda->sqld; fc_i++)
	{
		fc_sqlda->sqlvar[fc_i].sqltype = sqlda_dynamic_type(PQftype(fc_res, fc_i), fc_compat);
		strcpy(fc_fname, PQfname(fc_res, fc_i));
		fc_sqlda->sqlvar[fc_i].sqlname = fc_fname;
		fc_fname += strlen(fc_sqlda->sqlvar[fc_i].sqlname) + 1;

		/*
		 * 这部分保留供将来使用，因此
		 * 暂时保持为空
		 */
		/* sqlda->sqlvar[i].sqlformat = (char *) (long) PQfformat(res, i); */
		fc_sqlda->sqlvar[fc_i].sqlxid = PQftype(fc_res, fc_i);
		fc_sqlda->sqlvar[fc_i].sqltypelen = PQfsize(fc_res, fc_i);
	}

	return fc_sqlda;
}

/*
 * 从 PGresult 设置值。
 */
static int16 value_is_null = -1;
static int16 value_is_not_null = 0;

void ecpg_set_compat_sqlda(int fc_lineno, struct sqlda_compat **fc__sqlda, const PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat)
{
	struct sqlda_compat *fc_sqlda = (*fc__sqlda);
	int			fc_i;
	long		fc_offset,
				fc_next_offset;

	if (fc_row < 0)
		return;

	/* 第一个字段值的偏移 */
	fc_offset = fc_sqlda_compat_empty_size(fc_res);

	/*
	 * 设置 sqlvar[i]->sqldata 指针并将值转换为正确的格式
	 */
	for (fc_i = 0; fc_i < fc_sqlda->sqld; fc_i++)
	{
		int			fc_isnull;
		int			fc_datalen;
		bool		fc_set_data = true;

		switch (fc_sqlda->sqlvar[fc_i].sqltype)
		{
			case ECPGt_short:
			case ECPGt_unsigned_short:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(short), sizeof(short), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(short);
				break;
			case ECPGt_int:
			case ECPGt_unsigned_int:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), sizeof(int), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(int);
				break;
			case ECPGt_long:
			case ECPGt_unsigned_long:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(long), sizeof(long), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(long);
				break;
			case ECPGt_long_long:
			case ECPGt_unsigned_long_long:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(long long), sizeof(long long), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(long long);
				break;
			case ECPGt_bool:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(bool), sizeof(bool), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(bool);
				break;
			case ECPGt_float:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(float), sizeof(float), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(float);
				break;
			case ECPGt_double:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(double), sizeof(double), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(double);
				break;
			case ECPGt_decimal:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), sizeof(decimal), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(decimal);
				break;
			case ECPGt_numeric:
				{
					numeric    *fc_num;
					char	   *fc_val;

					fc_set_data = false;

					fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(NumericDigit *), sizeof(numeric), &fc_offset, &fc_next_offset);
					fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
					fc_sqlda->sqlvar[fc_i].sqllen = sizeof(numeric);

					if (PQgetisnull(fc_res, fc_row, fc_i))
					{
						ECPGset_noind_null(ECPGt_numeric, fc_sqlda->sqlvar[fc_i].sqldata);
						break;
					}

					fc_val = PQgetvalue(fc_res, fc_row, fc_i);
					fc_num = PGTYPESnumeric_from_asc(fc_val, NULL);
					if (!fc_num)
					{
						ECPGset_noind_null(ECPGt_numeric, fc_sqlda->sqlvar[fc_i].sqldata);
						break;
					}

					memcpy(fc_sqlda->sqlvar[fc_i].sqldata, fc_num, sizeof(numeric));

					if (fc_num->buf)
					{
						fc_ecpg_sqlda_align_add_size(fc_next_offset, sizeof(int), fc_num->digits - fc_num->buf + fc_num->ndigits, &fc_offset, &fc_next_offset);
						memcpy((char *) fc_sqlda + fc_offset, fc_num->buf, fc_num->digits - fc_num->buf + fc_num->ndigits);

						((numeric *) fc_sqlda->sqlvar[fc_i].sqldata)->buf = (NumericDigit *) fc_sqlda + fc_offset;
						((numeric *) fc_sqlda->sqlvar[fc_i].sqldata)->digits = (NumericDigit *) fc_sqlda + fc_offset + (fc_num->digits - fc_num->buf);
					}

					PGTYPESnumeric_free(fc_num);

					break;
				}
			case ECPGt_date:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(date), sizeof(date), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(date);
				break;
			case ECPGt_timestamp:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int64), sizeof(timestamp), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(timestamp);
				break;
			case ECPGt_interval:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int64), sizeof(interval), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(interval);
				break;
			case ECPGt_char:
			case ECPGt_unsigned_char:
			case ECPGt_string:
			default:
				fc_datalen = strlen(PQgetvalue(fc_res, fc_row, fc_i)) + 1;
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), fc_datalen, &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = fc_datalen;
				if (fc_datalen > 32768)
					fc_sqlda->sqlvar[fc_i].sqlilongdata = fc_sqlda->sqlvar[fc_i].sqldata;
				break;
		}

		fc_isnull = PQgetisnull(fc_res, fc_row, fc_i);
		ecpg_log("ecpg_set_compat_sqlda on line %d row %d col %d %s\n", fc_lineno, fc_row, fc_i, fc_isnull ? "IS NULL" : "IS NOT NULL");
		fc_sqlda->sqlvar[fc_i].sqlind = fc_isnull ? &value_is_null : &value_is_not_null;
		fc_sqlda->sqlvar[fc_i].sqlitype = ECPGt_short;
		fc_sqlda->sqlvar[fc_i].sqlilen = sizeof(short);
		if (!fc_isnull)
		{
			if (fc_set_data)
				ecpg_get_data(fc_res, fc_row, fc_i, fc_lineno,
							  fc_sqlda->sqlvar[fc_i].sqltype, ECPGt_NO_INDICATOR,
							  fc_sqlda->sqlvar[fc_i].sqldata, NULL, 0, 0, 0,
							  ECPG_ARRAY_NONE, fc_compat, false);
		}
		else
			ECPGset_noind_null(fc_sqlda->sqlvar[fc_i].sqltype, fc_sqlda->sqlvar[fc_i].sqldata);

		fc_offset = fc_next_offset;
	}
}

struct sqlda_struct *
ecpg_build_native_sqlda(int fc_line, PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat)
{
	struct sqlda_struct *fc_sqlda;
	long		fc_size;
	int			fc_i;

	fc_size = fc_sqlda_native_total_size(fc_res, fc_row, fc_compat);
	fc_sqlda = (struct sqlda_struct *) ecpg_alloc(fc_size, fc_line);
	if (!fc_sqlda)
		return NULL;

	memset(fc_sqlda, 0, fc_size);

	sprintf(fc_sqlda->sqldaid, "SQLDA  ");
	fc_sqlda->sqld = fc_sqlda->sqln = PQnfields(fc_res);
	ecpg_log("ecpg_build_native_sqlda on line %d sqld = %d\n", fc_line, fc_sqlda->sqld);
	fc_sqlda->sqldabc = sizeof(struct sqlda_struct) + (fc_sqlda->sqld - 1) * sizeof(struct sqlvar_struct);

	for (fc_i = 0; fc_i < fc_sqlda->sqld; fc_i++)
	{
		char	   *fc_fname;

		fc_sqlda->sqlvar[fc_i].sqltype = sqlda_dynamic_type(PQftype(fc_res, fc_i), fc_compat);
		fc_fname = PQfname(fc_res, fc_i);
		fc_sqlda->sqlvar[fc_i].sqlname.length = strlen(fc_fname);
		strcpy(fc_sqlda->sqlvar[fc_i].sqlname.data, fc_fname);
	}

	return fc_sqlda;
}

void ecpg_set_native_sqlda(int fc_lineno, struct sqlda_struct **fc__sqlda, const PGresult *fc_res, int fc_row, enum COMPAT_MODE fc_compat)
{
	struct sqlda_struct *fc_sqlda = (*fc__sqlda);
	int			fc_i;
	long		fc_offset,
				fc_next_offset;

	if (fc_row < 0)
		return;

	/* 第一个字段值的偏移 */
	fc_offset = fc_sqlda_native_empty_size(fc_res);

	/*
	 * 设置 sqlvar[i]->sqldata 指针并将值转换为正确的格式
	 */
	for (fc_i = 0; fc_i < fc_sqlda->sqld; fc_i++)
	{
		int			fc_isnull;
		int			fc_datalen;
		bool		fc_set_data = true;

		switch (fc_sqlda->sqlvar[fc_i].sqltype)
		{
			case ECPGt_short:
			case ECPGt_unsigned_short:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(short), sizeof(short), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(short);
				break;
			case ECPGt_int:
			case ECPGt_unsigned_int:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), sizeof(int), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(int);
				break;
			case ECPGt_long:
			case ECPGt_unsigned_long:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(long), sizeof(long), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(long);
				break;
			case ECPGt_long_long:
			case ECPGt_unsigned_long_long:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(long long), sizeof(long long), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(long long);
				break;
			case ECPGt_bool:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(bool), sizeof(bool), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(bool);
				break;
			case ECPGt_float:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(float), sizeof(float), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(float);
				break;
			case ECPGt_double:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(double), sizeof(double), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(double);
				break;
			case ECPGt_decimal:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), sizeof(decimal), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(decimal);
				break;
			case ECPGt_numeric:
				{
					numeric    *fc_num;
					char	   *fc_val;

					fc_set_data = false;

					fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(NumericDigit *), sizeof(numeric), &fc_offset, &fc_next_offset);
					fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
					fc_sqlda->sqlvar[fc_i].sqllen = sizeof(numeric);

					if (PQgetisnull(fc_res, fc_row, fc_i))
					{
						ECPGset_noind_null(ECPGt_numeric, fc_sqlda->sqlvar[fc_i].sqldata);
						break;
					}

					fc_val = PQgetvalue(fc_res, fc_row, fc_i);
					fc_num = PGTYPESnumeric_from_asc(fc_val, NULL);
					if (!fc_num)
					{
						ECPGset_noind_null(ECPGt_numeric, fc_sqlda->sqlvar[fc_i].sqldata);
						break;
					}

					memcpy(fc_sqlda->sqlvar[fc_i].sqldata, fc_num, sizeof(numeric));

					if (fc_num->buf)
					{
						fc_ecpg_sqlda_align_add_size(fc_next_offset, sizeof(int), fc_num->digits - fc_num->buf + fc_num->ndigits, &fc_offset, &fc_next_offset);
						memcpy((char *) fc_sqlda + fc_offset, fc_num->buf, fc_num->digits - fc_num->buf + fc_num->ndigits);

						((numeric *) fc_sqlda->sqlvar[fc_i].sqldata)->buf = (NumericDigit *) fc_sqlda + fc_offset;
						((numeric *) fc_sqlda->sqlvar[fc_i].sqldata)->digits = (NumericDigit *) fc_sqlda + fc_offset + (fc_num->digits - fc_num->buf);
					}

					PGTYPESnumeric_free(fc_num);

					break;
				}
			case ECPGt_date:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(date), sizeof(date), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(date);
				break;
			case ECPGt_timestamp:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int64), sizeof(timestamp), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(timestamp);
				break;
			case ECPGt_interval:
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int64), sizeof(interval), &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = sizeof(interval);
				break;
			case ECPGt_char:
			case ECPGt_unsigned_char:
			case ECPGt_string:
			default:
				fc_datalen = strlen(PQgetvalue(fc_res, fc_row, fc_i)) + 1;
				fc_ecpg_sqlda_align_add_size(fc_offset, sizeof(int), fc_datalen, &fc_offset, &fc_next_offset);
				fc_sqlda->sqlvar[fc_i].sqldata = (char *) fc_sqlda + fc_offset;
				fc_sqlda->sqlvar[fc_i].sqllen = fc_datalen;
				break;
		}

		fc_isnull = PQgetisnull(fc_res, fc_row, fc_i);
		ecpg_log("ecpg_set_native_sqlda on line %d row %d col %d %s\n", fc_lineno, fc_row, fc_i, fc_isnull ? "IS NULL" : "IS NOT NULL");
		fc_sqlda->sqlvar[fc_i].sqlind = fc_isnull ? &value_is_null : &value_is_not_null;
		if (!fc_isnull)
		{
			if (fc_set_data)
				ecpg_get_data(fc_res, fc_row, fc_i, fc_lineno,
							  fc_sqlda->sqlvar[fc_i].sqltype, ECPGt_NO_INDICATOR,
							  fc_sqlda->sqlvar[fc_i].sqldata, NULL, 0, 0, 0,
							  ECPG_ARRAY_NONE, fc_compat, false);
		}

		fc_offset = fc_next_offset;
	}
}
